package com.upb.webserver.fisco.chain;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.upb.webserver.chain.ChainManager;
import com.upb.webserver.chainhost.ChainHostService;
import com.upb.webserver.chainorg.ChainOrgService;
import com.upb.webserver.common.constant.Constant;
import com.upb.webserver.common.enums.ChainOrAppStatusEnum;
import com.upb.webserver.common.enums.ChainTypeEnum;
import com.upb.webserver.common.enums.DockerImageTypeEnum;
import com.upb.webserver.common.enums.OrgTypeEnum;
import com.upb.webserver.common.exception.WebServerException;
import com.upb.webserver.common.pojo.base.BaseResponse;
import com.upb.webserver.common.pojo.chain.ReqAddRunningFiscoChainVO;
import com.upb.webserver.common.pojo.order.ReqAddNewNode2ChainVO;
import com.upb.webserver.common.pojo.order.ReqAddWorkOrderOfNewChainVO;
import com.upb.webserver.common.pojo.order.RespAddNodeVO;
import com.upb.webserver.common.pojo.rest.fisco.chainmgr.req.ReqAddFrontInfo;
import com.upb.webserver.common.pojo.rest.fisco.chainmgr.req.ReqAddRunningFiscoChainInfo;
import com.upb.webserver.common.pojo.rest.fisco.chainmgr.req.ReqDeployFiscoChain;
import com.upb.webserver.common.pojo.rest.fisco.chainmgr.req.ReqFrontIpList;
import com.upb.webserver.common.properties.WebServerProperties;
import com.upb.webserver.common.returncode.ConstantCode;
import com.upb.webserver.common.tools.JsonUtils;
import com.upb.webserver.dao.entity.TbChain;
import com.upb.webserver.dao.entity.TbHost;
import com.upb.webserver.dao.entity.TbOrg;
import com.upb.webserver.dao.mapper.TbChainMapper;
import com.upb.webserver.dao.mapper.TbHostMapper;
import com.upb.webserver.dao.mapper.TbOrgMapper;
import com.upb.webserver.host.HostManager;
import com.upb.webserver.org.OrgManager;
import com.upb.webserver.rest.RespInitHost;
import com.upb.webserver.rest.fisco.FiscoChainMgrRestService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * save fisco chain info from web, rest fisco subsystem to deploy chain
 */
@Slf4j
@Service
public class FiscoChainService {
    @Autowired
    private WebServerProperties webServerProperties;
    @Autowired
    private TbChainMapper tbChainMapper;
    @Autowired
    private ChainHostService chainHostService;
    @Autowired
    private ChainOrgService chainOrgService;
    @Autowired
    private TbHostMapper hostMapper;
    @Autowired
    private ChainManager chainManager;
    @Autowired
    private FiscoChainMgrRestService fiscoChainMgrRestService;
    @Autowired
    private HostManager hostManager;
    @Autowired
    private OrgManager orgManager;
    @Autowired
    private TbOrgMapper orgMapper;


    /**
     * 提取到fiscochainservice
     *
     * @param fromPlatform
     * @param currentAccountId
     * @param param
     * @return
     */
    @Transactional
    public BaseResponse addRunningFiscoChain(Byte fromPlatform, int currentAccountId, ReqAddRunningFiscoChainVO param) {
        log.info("start exec method [addRunningFiscoChain].  fromPlatform:{} currentAccountId:{}  param:{}", fromPlatform, currentAccountId, JsonUtils.objToString(param));

        //check param
        chainManager.checkBeforeAddNewChain(ChainTypeEnum.FISCO.getId(), param.getChainName());
        //save
        TbChain tbChain = chainManager.saveNewChain(fromPlatform, currentAccountId, param.getChainName(), param.getChainVersion(), ChainTypeEnum.FISCO, param.getEncryptionId(), StringUtils.isNotEmpty(param.getDescription()) ? param.getDescription() : null);

        //init front list
        List<ReqAddFrontInfo> restFrontList = new ArrayList<>();
        for (ReqAddRunningFiscoChainVO.FrontInfoVO frontInfoVO : param.getFrontInfoVOList()) {
            if (!param.getOrgIdSet().contains(frontInfoVO.getOrgId())) {
                log.warn("fail exec method [addRunningFiscoChain]. orgIdInFront:{} but OrgIdSet:{}", frontInfoVO.getOrgId(), JsonUtils.objToString(param.getOrgIdSet()));
                throw new WebServerException(ConstantCode.INVALID_ORG_ID_OF_FRONT);
            }

            //save chain-org
            chainOrgService.saveChainOrg(tbChain.getId(), frontInfoVO.getOrgId(), OrgTypeEnum.FISCO_ORG);
            //check hostId
            TbHost tbHost = hostManager.verifyHostId(frontInfoVO.getHostId());
            TbOrg tbOrg = orgManager.verifyOrgId(frontInfoVO.getOrgId());
            //init restFront
            ReqAddFrontInfo restFront = new ReqAddFrontInfo();
            restFront.setExtHostId(frontInfoVO.getHostId());
            restFront.setExtCompanyId(tbHost.getCompanyId());
            restFront.setExtOrgId(tbOrg.getId());
            restFront.setChainId(tbChain.getId());
            restFront.setFrontIp(tbHost.getIp());
            restFront.setFrontPort(frontInfoVO.getFrontPort());

            restFrontList.add(restFront);
        }

        //save chain-org
        param.getOrgIdSet().stream().forEach(orgId -> {
            //check orgId
            orgManager.verifyOrgId(orgId);
            chainOrgService.saveChainOrg(tbChain.getId(), orgId, OrgTypeEnum.FISCO_ORG);
        });

        if (CollectionUtil.isEmpty(restFrontList)) {
            log.warn("fail exec method [addRunningFiscoChain]. restFrontList is empty");
            throw new WebServerException(ConstantCode.FRONT_LIST_IS_EMPTY);
        }
        //save chain-host TODO 需要优化，可能用户不会填所有的host
        Map<Integer, Long> frontGroupMap = restFrontList.stream().map(f -> f.getExtHostId()).collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
        frontGroupMap.keySet().stream().forEach(hostId -> {
            chainHostService.saveChainHost(tbChain.getId(), hostId, frontGroupMap.get(hostId).intValue());
        });


        //init ReqAddRunningFiscoChainInfo
        ReqAddRunningFiscoChainInfo restParam = new ReqAddRunningFiscoChainInfo();
        BeanUtils.copyProperties(param, restParam);
        restParam.setChainType(Integer.valueOf(param.getEncryptionId()));
        restParam.setChainId(tbChain.getId());
        restParam.setFrontList(restFrontList);

        BaseResponse baseResponse = fiscoChainMgrRestService.addRunningFiscoChain(restParam);
        log.info("baseResponse:{}", JsonUtils.objToString(baseResponse));
        if (!baseResponse.isSuccess()) {
            log.warn("fail exec method [addRunningFiscoChain]. baseResponse:{}", JsonUtils.objToString(baseResponse));
            throw new WebServerException(baseResponse.getCode(), baseResponse.getMessage());
        }


        //update status
        tbChain.setGmtModified(new Date());
        tbChain.setCreateStatus(ChainOrAppStatusEnum.RUNNING.getId());
        tbChainMapper.updateByPrimaryKey(tbChain);
        log.warn("success exec method [addRunningFiscoChain]");

        return baseResponse;
    }


    /**
     * @param chainDetail
     */
    @Transactional
    public Integer fiscoDeploy(Byte platform, int creatorId, Byte dockerImageType, ReqAddWorkOrderOfNewChainVO chainDetail) {
        if (chainDetail == null || chainDetail.getChainType() != ChainTypeEnum.FISCO.getId()) {
            log.warn("Not fisco deploy work order.");
            return null;
        }
        if (dockerImageType == null || DockerImageTypeEnum.getById(dockerImageType) == null) {
            log.warn("fail exec method [fiscoDeploy]. not support dockerImageType:{}", dockerImageType);
            throw new WebServerException(ConstantCode.PARAM_EXCEPTION.msg("not support the dockerImageType"));
        }
        log.info("start exec method [fiscoDeploy]. dockerImageType:{} chainDetail:{} ", dockerImageType, JsonUtils.objToString(chainDetail));

        //check history deploy status
        boolean wasDeployedFail = chainManager.wasChainDeployedFail(chainDetail.getName());
        TbChain chainOnDb = tbChainMapper.selectByChainName(chainDetail.getName());
        if (wasDeployedFail) {
            fiscoChainMgrRestService.deleteIfRemoteChainExist(chainOnDb.getId());
        }

        //save to tb_chain
        TbChain tbChain = chainManager.saveNewChain(platform, creatorId, chainDetail.getName(), chainDetail.getVersion(), ChainTypeEnum.FISCO, chainDetail.getEncryptionId(), StringUtils.isNotEmpty(chainDetail.getDescription()) ? chainDetail.getDescription() : null);
        BaseResponse deployRsp = null;

        try {
            //deploy param
            ReqDeployFiscoChain reqDeployFiscoChain = initDeployParam(chainDetail, tbChain.getId());
            reqDeployFiscoChain.setDockerImageType(dockerImageType);
            //check org host
            checkOrgHost(reqDeployFiscoChain);
            //init host port
            initPortOfHost(reqDeployFiscoChain);
            //deploy
            deployRsp = fiscoChainMgrRestService.deployWeBASEChain(reqDeployFiscoChain);
            log.info("deployRspJson:{}", JsonUtils.objToString(deployRsp));
        } catch (WebServerException ex) {
            log.error("WebServerException.fail exec deploy chain to remote server", ex);
            throw ex;
//            tbChain.setRemark(ex.getRetCode().getMessage());
//            tbChain.setCreateStatus(ChainOrAppStatusEnum.DEPLOY_FAIL.getId());
//            tbChainMapper.updateByPrimaryKeyWithBLOBs(tbChain);
//            throw OperationException.fromWebServerException(ex);
        } catch (Exception ex) {
            log.error("Exception.fail exec deploy chain to remote server", ex);
            throw new WebServerException(ConstantCode.CHAIN_DEPLOY_FAIL.msg(ex.getMessage()));
//            tbChain.setRemark(ex.getMessage());
//            tbChain.setCreateStatus(ChainOrAppStatusEnum.DEPLOY_FAIL.getId());
//            tbChainMapper.updateByPrimaryKeyWithBLOBs(tbChain);
//            throw new OperationException(ConstantCode.CHAIN_DEPLOY_FAIL.msg(ex.getMessage()));
        }

        //update chain
        if (!deployRsp.isSuccess()) {
            log.error("Deploy chain fail. deployRsp :{}", JsonUtils.objToString(deployRsp));
            throw new WebServerException(deployRsp.getCode(), deployRsp.getMessage());
//            tbChain.setRemark(deployRsp.getMessage());
//            tbChain.setGmtModified(new Date());
//            tbChain.setCreateStatus(ChainOrAppStatusEnum.DEPLOY_FAIL.getId());
//            tbChainMapper.updateByPrimaryKeyWithBLOBs(tbChain);
//            throw new OperationException(deployRsp.getCode(), deployRsp.getMessage());
        }

        //success
        tbChain.setGmtModified(new Date());
        tbChain.setRemark("");
        tbChainMapper.updateByPrimaryKeyWithBLOBs(tbChain);

        return tbChain.getId();
    }

    private void checkOrgHost(ReqDeployFiscoChain reqDeployFiscoChain){
        List<ReqDeployFiscoChain.DeployHost> deployHostList = reqDeployFiscoChain.getDeployHostList();
        Map<String, Integer> orgIpMap = new ConcurrentHashMap<>();
        for (ReqDeployFiscoChain.DeployHost deployHost : deployHostList) {
            if (CollectionUtil.isEmpty(orgIpMap)) {
                orgIpMap.put(deployHost.getIp(), deployHost.getExtOrgId());
            } else {
                if (orgIpMap.get(deployHost.getIp()) == null) {
                    orgIpMap.put(deployHost.getIp(), deployHost.getExtOrgId());
                } else {
                    if (orgIpMap.get(deployHost.getIp()) != deployHost.getExtHostId()) {
                        throw new WebServerException(ConstantCode.HOST_IP_CANNOT_SAME.getCode(), ConstantCode.HOST_IP_CANNOT_SAME.getMessage());
                    }
                }
            }
        }
    }


    //第一个String是端口类型，包括：rpc、p2p、channel、front
    //第二个String是ip,如：127.0.0.1
    //最后一个Integer是对应ip当前最大的port
    ThreadLocal<Map<String, Map<String, Integer>>> threadLocal = ThreadLocal.withInitial(() -> new ConcurrentHashMap<>());

    /**
     * @param param
     */
    private void initPortOfHost(ReqDeployFiscoChain param) {
        log.info("start exec method [initPortOfHost]. param:{} ", JsonUtils.objToString(param));
        if (Objects.isNull(param) || CollectionUtil.isEmpty(param.getDeployHostList())) return;

        Map<String, Map<String, Integer>> ip_max_port_map = threadLocal.get();
        try {
            for (ReqDeployFiscoChain.DeployHost host : param.getDeployHostList()) {
                Map<String, Integer> portMap = getMaxPort(host);
                initHostPort(host, ip_max_port_map, portMap);
            }
        } finally {
            threadLocal.remove();
        }
    }

    private void initHostPort(ReqDeployFiscoChain.DeployHost host, Map<String, Map<String, Integer>> ip_max_port_map, Map<String, Integer> portMap) {
        host.setJsonrpcPort(getNextStepPort(Constant.PORT_TYPE_RPC, host, ip_max_port_map, portMap));
        host.setP2pPort(getNextStepPort(Constant.PORT_TYPE_P2P, host, ip_max_port_map, portMap));
        host.setChannelPort(getNextStepPort(Constant.PORT_TYPE_CHANNEL, host, ip_max_port_map, portMap));
        host.setFrontPort(getNextStepPort(Constant.PORT_TYPE_FRONT, host, ip_max_port_map, portMap));
    }


    /**
     * 获取下一阶段的其实端口号（每个阶段的端口号间隔10）
     *
     * @param portType
     * @param host
     * @param ip_max_port_map
     * @return
     */
    private int getNextStepPort(String portType, ReqDeployFiscoChain.DeployHost host, Map<String, Map<String, Integer>> ip_max_port_map, Map<String, Integer> portMap) {
        log.info("start exec method [getNextStepPort]. portType:{} host:{}", portType, JsonUtils.objToString(host));
        //get default new port
        String hostIp = host.getIp();
        Map<String, Integer> ip_port_map = ip_max_port_map.get(portType);
        int theOldStartPort = Optional.ofNullable(CollectionUtil.isNotEmpty(ip_port_map) ? ip_port_map.get(hostIp) : 0).orElse(0);
        int theNewStartPort = 0;
        if (CollectionUtil.isEmpty(portMap)) {
            theNewStartPort = theOldStartPort == 0 ? caseDefaultPort(portType) : theOldStartPort + Constant.PORT_ADD_STEP;
        } else {
            theNewStartPort = portMap.get(portType) + 1;
        }

        if (host.getNum() > Constant.PORT_ADD_STEP) {
            log.warn("fail exec method[getNextStepPort]. host:{} orgId:{}, input nodeCount:{} but support:{}", hostIp, host.getExtOrgId(), host.getNum(), Constant.PORT_ADD_STEP);
            throw new WebServerException(ConstantCode.TOO_MANY_NODE_COUNT);
        }

        //get nodeList by nodeCount
        log.debug("theNewStartPort:{}", theNewStartPort);
        List<Integer> portList = CollectionUtil.toList(theNewStartPort);
        while (portList.size() < host.getNum()) portList.add(CollectionUtil.getLast(portList) + 1);

        //check port 在chain-manager检查
//        Pair<Boolean, Integer> portReachable = NetTools.anyPortInUse(hostIp, host.getSshUser(), host.getSshPort(), webServerProperties.getSshPrivateKey(), portList);
//        if (portReachable.getKey()) {
//            log.error("fail exec method[getNextStepPorts] Port:[{}] is in use on host :[{}] failed", portReachable.getValue(), hostIp);
//            throw new WebServerException(ConstantCode.PORT_HAS_BEAN_IN_USE.msg(String.format("Port:%1d is in use on host :%2s failed", portReachable.getValue(), hostIp)));
//        }

        //reset ip_max_port_map
        log.info("success exec method [getNextPort]. portType:{} hostIp:{}, resultNewStartPort:{}", portType, hostIp, theNewStartPort);
//        ip_port_map.put(hostIp, theNewStartPort);
//        ip_max_port_map.put(portType, ip_port_map);
        if (CollectionUtil.isEmpty(ip_port_map)) {
            ip_max_port_map.put(portType, MapUtil.of(hostIp, theNewStartPort));
        } else {
            ip_port_map.put(hostIp, theNewStartPort);
        }

        return theNewStartPort;
    }

    private Map<String, Integer> getMaxPort(ReqDeployFiscoChain.DeployHost host) {
        String hostIp = host.getIp();
        ReqFrontIpList reqFrontIpList = new ReqFrontIpList();
        List<String> ipList = new ArrayList<>();
        ipList.add(hostIp);
        reqFrontIpList.setFrontIpList(ipList);
        BaseResponse restRsp = fiscoChainMgrRestService.getListFrontByIp(reqFrontIpList);
        Map<String, Integer> frontMap = new ConcurrentHashMap<>();
        if (restRsp.getData() != null) {
            JSONArray jsonArray = new JSONArray(restRsp.getData());
            for (int i = 0; i < jsonArray.size(); i++) {
                JSONObject object = jsonArray.getJSONObject(i);
                if (CollectionUtil.isEmpty(frontMap)) {
                    frontMap.put(Constant.PORT_TYPE_RPC, Integer.parseInt(String.valueOf(object.get(Constant.PORT_TYPE_JSONRPCPORT))));
                    frontMap.put(Constant.PORT_TYPE_P2P, Integer.parseInt(String.valueOf(object.get(Constant.PORT_TYPE_P2PPORT))));
                    frontMap.put(Constant.PORT_TYPE_CHANNEL, Integer.parseInt(String.valueOf(object.get(Constant.PORT_TYPE_CHANNELPORT))));
                    frontMap.put(Constant.PORT_TYPE_FRONT, Integer.parseInt(String.valueOf(object.get(Constant.PORT_TYPE_FRONTPORT))));
                } else {
                    frontMap.put(Constant.PORT_TYPE_RPC, frontMap.get(Constant.PORT_TYPE_RPC) - Integer.parseInt(String.valueOf(object.get(Constant.PORT_TYPE_JSONRPCPORT))) > 0 ? frontMap.get(Constant.PORT_TYPE_RPC) : Integer.parseInt(String.valueOf(object.get(Constant.PORT_TYPE_JSONRPCPORT))));
                    frontMap.put(Constant.PORT_TYPE_P2P, frontMap.get(Constant.PORT_TYPE_P2P) - Integer.parseInt(String.valueOf(object.get(Constant.PORT_TYPE_P2PPORT))) > 0 ? frontMap.get(Constant.PORT_TYPE_P2P) : Integer.parseInt(String.valueOf(object.get(Constant.PORT_TYPE_P2PPORT))));
                    frontMap.put(Constant.PORT_TYPE_CHANNEL, frontMap.get(Constant.PORT_TYPE_CHANNEL) - Integer.parseInt(String.valueOf(object.get(Constant.PORT_TYPE_CHANNELPORT))) > 0 ? frontMap.get(Constant.PORT_TYPE_CHANNEL) : Integer.parseInt(String.valueOf(object.get(Constant.PORT_TYPE_CHANNELPORT))));
                    frontMap.put(Constant.PORT_TYPE_FRONT, frontMap.get(Constant.PORT_TYPE_FRONT) - Integer.parseInt(String.valueOf(object.get(Constant.PORT_TYPE_FRONTPORT))) > 0 ? frontMap.get(Constant.PORT_TYPE_FRONT) : Integer.parseInt(String.valueOf(object.get(Constant.PORT_TYPE_FRONTPORT))));
                }
            }
        }
        return frontMap;
    }


    /**
     * @param type
     * @return
     */
    private int caseDefaultPort(String type) {
        switch (type) {
            case Constant.PORT_TYPE_RPC:
                return Constant.FISCO_DEFAULT_JSON_RPC_PORT;
            case Constant.PORT_TYPE_P2P:
                return Constant.FISCO_DEFAULT_P2P_PORT;
            case Constant.PORT_TYPE_CHANNEL:
                return Constant.FISCO_DEFAULT_CHANEL_PORT;
            case Constant.PORT_TYPE_FRONT:
                return Constant.FISCO_DEFAULT_FRONT_PORT;
        }
        log.warn("fail exec method[caseDefaultPort], not config type:{}, so return default port value:8888", type);
        return 8888;
    }


    /**
     * @param chainDetail
     * @param chainId     如果chainId小于0，则不保存数据到db
     * @return
     */
    //    @Transactional(noRollbackFor = WebServerException.class)
    @Transactional
    public ReqDeployFiscoChain initDeployParam(ReqAddWorkOrderOfNewChainVO chainDetail, int chainId) {
        //deploy param
        ReqDeployFiscoChain reqDeployFiscoChain = new ReqDeployFiscoChain();
        BeanUtils.copyProperties(chainDetail, reqDeployFiscoChain);
        reqDeployFiscoChain.setChainId(chainId);
        reqDeployFiscoChain.setDockerImageType(DockerImageTypeEnum.DOWNLOAD_CDN.getId());
        reqDeployFiscoChain.setEncryptType(chainDetail.getEncryptionId());
//        reqDeployFiscoChain.setChainName(chainDetail.getName());  如果不传，就用chainId

        //host list
        List<ReqDeployFiscoChain.DeployHost> hostList = new ArrayList<>();
        chainDetail.getFiscoOrgList().stream().forEach(o -> {
            List<ReqDeployFiscoChain.DeployHost> hostListOnOrg = ((FiscoChainService) AopContext.currentProxy()).getDeployHostFromNewFiscoChainOrg(chainId, o);
            hostList.addAll(hostListOnOrg);
        });
        reqDeployFiscoChain.setDeployHostList(hostList);
        return reqDeployFiscoChain;
    }


    /**
     * @param chainId   如果chainId小于0，则不保存数据到db
     * @param sourceOrg
     * @return
     */
//    //    @Transactional(noRollbackFor = WebServerException.class)
    @Transactional
    public List<ReqDeployFiscoChain.DeployHost> getDeployHostFromNewFiscoChainOrg(int chainId, ReqAddWorkOrderOfNewChainVO.NewFiscoChainOrg sourceOrg) {
        //save org
        if (chainId > 0)
            chainOrgService.saveChainOrg(chainId, sourceOrg.getOrgId(), OrgTypeEnum.FISCO_ORG);

        List<ReqDeployFiscoChain.DeployHost> targetHostList = new ArrayList<>();
        for (ReqAddWorkOrderOfNewChainVO.NewFiscoChainHost sourceHost : sourceOrg.getFiscoHostList()) {
            //save chain host
            if (chainId > 0)
                chainHostService.saveChainHost(chainId, sourceHost.getFiscoHostId(), sourceHost.getNodeCount());

            ReqDeployFiscoChain.DeployHost targetHost = new ReqDeployFiscoChain.DeployHost();
            TbHost tbHost = hostMapper.selectByPrimaryKey(sourceHost.getFiscoHostId());
            if (tbHost != null) {
                BeanUtils.copyProperties(tbHost, targetHost);
                targetHost.setExtCompanyId(sourceOrg.getCompanyId());
                targetHost.setExtOrgId(sourceOrg.getOrgId());
                targetHost.setExtHostId(sourceHost.getFiscoHostId());
                targetHost.setNum(sourceHost.getNodeCount());
                targetHost.setRootDirOnHost(tbHost.getRootDir());
                targetHostList.add(targetHost);
            }
        }
        return targetHostList;
    }


    /**
     * @param dockerImageType
     * @param chainDetail
     * @return
     */
    public List<RespInitHost> initNodeHostList(Byte dockerImageType, ReqAddWorkOrderOfNewChainVO chainDetail) {
        log.info("start exec method [initNodeHostList]. dockerImageType:{} chainDetail:{}", dockerImageType, JsonUtils.objToString(chainDetail));

        //deploy param
        ReqDeployFiscoChain reqDeployFiscoChain = initDeployParam(chainDetail, -1);
        reqDeployFiscoChain.setDockerImageType(dockerImageType);
        //init host port param
        initPortOfHost(reqDeployFiscoChain);
        List<RespInitHost> resultList = fiscoChainMgrRestService.nodeHostListInit(reqDeployFiscoChain);
        log.info("finish exec method [initNodeHostList]. result:{}", JsonUtils.objToString(resultList));
        return resultList;
    }

    public BaseResponse addNode(ReqAddNewNode2ChainVO reqAddNewNode2ChainVO) {
        RespAddNodeVO respAddNodeVO = new RespAddNodeVO();
        //根据fiscoHostId查询节点详情
        List<ReqDeployFiscoChain.DeployHost> list = new ArrayList<>();
        Map<String, Map<String, Integer>> ip_max_port_map = threadLocal.get();
        try {
            for (ReqAddWorkOrderOfNewChainVO.NewFiscoChainHost newFiscoChainHost : reqAddNewNode2ChainVO.getFiscoHostList()) {
                TbHost tbHost = hostMapper.selectByPrimaryKey(newFiscoChainHost.getFiscoHostId());
                if (tbHost != null) {
                    ReqDeployFiscoChain.DeployHost hostInfo = new ReqDeployFiscoChain.DeployHost();
                    hostInfo.setExtCompanyId(tbHost.getCompanyId());
                    hostInfo.setExtOrgId(tbHost.getOrgId());
                    hostInfo.setExtHostId(tbHost.getId());
                    hostInfo.setIp(tbHost.getIp());
                    hostInfo.setSshUser(tbHost.getSshUser());
                    hostInfo.setSshPort(tbHost.getSshPort());
                    //hostInfo.set("dockerDemonPort", tbHost.getDockerPort());
                    hostInfo.setNum(newFiscoChainHost.getNodeCount());
                    hostInfo.setRootDirOnHost(tbHost.getRootDir());
                    Map<String, Integer> portMap = getMaxPort(hostInfo);
                    initHostPort(hostInfo, ip_max_port_map, portMap);
                    list.add(hostInfo);
                } else {
                    throw new WebServerException(ConstantCode.INVALID_HOST_ID);
                }
            }
        } finally {
            threadLocal.remove();
        }
        log.debug("exec method [fiscoChainMgrRestService.addNode]. list:{}", JsonUtils.objToString(list));
        return fiscoChainMgrRestService.addNode(reqAddNewNode2ChainVO, list);
    }
}

